<?php

/* ========================================================================= */
/* This file is part of the HypnoPHP Framework Library                       */
/* Copyright (c) 2009-2010, Radu Buzea                                       */
/* ========================================================================= */

/**
* @author Radu Buzea
* @copyright Copyright (c) 2009-2010, Radu Buzea (www.hypnosis.ro)
* @license GNU Lesser General Public License (www.gnu.org/copyleft/lesser.html)
* @package HypnoPHP
* @category Core
* @version 1.0.3
*/

// ============================================================================
// Final class: HValidation
// ============================================================================

final class HValidation
{

	/**
	* Email validation
	*
	* @param string $email The email address
	* @param bool $check_dns (optional) Check the email server DNS
	* @return bool
	*/
	static function email_validator($email = '', $check_dns = false)
	{
		if (!is_string($email)) { return false; }
		$a = strrpos($email, "@");
		if ($at_pos === false) { return false; }
		$domain = substr($email, $a + 1);
		$local = substr($email, 0, $a);
		$local_len = strlen($local);
		$domain_len = strlen($domain);
		// Check local part length, if is over 64 then the part length exceeded
		if ($local_len < 1 || $local_len > 64) { return false; }
		// Check domain part length, if is over 255 then the part length exceeded
		if ($domain_len < 1 || $domain_len > 255) { return false; }
		// Check first/last characters in the local part
		if ($local[0] == '.' || $local[$local_len-1] == '.') { return false; }
		// Check if local part has two consecutive dots
		if (preg_match('/\\.\\./', $local)) { return false; }
		// Check the characters from the domain part
		if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) { return false; }
		// Check if the domain part has two consecutive dots
		if (preg_match('/\\.\\./', $domain)) {return false; }
		if (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\", "", $local))) {
			// Character not valid in local part unless local part is quoted
			if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\", "", $local))) { return false; }
		}
		if ($check_dns && !(checkdnsrr($domain, "MX") || checkdnsrr($domain, "A"))) { return false; }
		return true;
	}

	# ###########################################################################

	/**
	* URL address validator
	*
	* @param string $value
	* @return bool
	*/
	static function url_validator(&$value = '')
	{
		if (!is_string($value)) { return false; }
		$value = trim($value);
		if ($value == '') { return false; }
		$regexp = "/^(([\w]+:)?\/\/)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,4}(:[\d]+)?(\/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&amp;?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?$/";
		return (preg_match($regexp, $value) !== false);
	}

	# ###########################################################################

	/**
	* String validator
	*
	* @param string $value (pass through)
	* @param integer $max_len (unsigned) the maximul length
	* @param bool $is_char (optional) is char type
	*/
	static function string_validator(&$value = '', $max_len = 0, $is_char = false)
	{
		if (!is_string($value)) { $value = strval($value); }
		if ($max_len > 0) {
			if (strlen($value) > $max_len) {
				$value = substr($value, 0, $max_len);
			} else if ($is_char) {
				while (strlen($value) < $max_len) { $value .= ' '; }
			}
		}
		return true;
	}

	# ###########################################################################

	/**
	* Date and time validator (MySQL format)
	*
	* @param string $value (pass through) the date / time string
	* @param string $type 'year', 'date', 'time', 'datetime'
	* @return bool
	*/
	static function datetime_validator(&$value = '', $type = '')
	{
		if (!is_string($value)) { return false; }
		$value = trim($value);
		$rexp_spc = "[\-\.\:\ ]{1,}";
		$rexp_4 = "([0-9]{2,4})";
		$rexp_2 = "([0-9]{2})";
		$rexp_date = "{$rexp_4}{$rexp_spc}{$rexp_2}{$rexp_spc}{$rexp_2}";
		$rexp_time = "{$rexp_2}{$rexp_spc}{$rexp_2}{$rexp_spc}{$rexp_2}";
		$type = trim(strtolower($type));
		switch ($type) {
			case 'date':
				$regexp = "/^{$rexp_date}$/";
				if (preg_match($regexp, $value, $r)) {
					if (isset($r[3])) {
						$y = intval($r[1]);
						$m = intval($r[2]);
						$d = intval($r[3]);
						if ($y == 0 && $m == 0 && $d == 0) {
							$value = '0000-00-00';
							return true;
						}
						if ($y < 100) {
							$y2k = (intval(date('Y')) - 2000) + 25;
							if ($y <= $y2k) { $y = intval('20' . $y); }
							else { $y = intval('19' . $y); }
						}
						$value = date('Y-m-d', mktime(0, 0, 0, $m, $d, $y));
						return true;
					}
				}
				break;
			case 'time':
				$regexp = "/^{$rexp_time}$/";
				if (preg_match($regexp, $value, $r)) {
					if (isset($r[3])) {
						$h = intval($r[1]);
						$m = intval($r[2]);
						$s = intval($r[3]);
						if ($h == 0 && $m == 0 && $s == 0) {
							$value = '00:00:00';
							return true;
						}
						if ($h < 0) { $h = 0; } elseif ($h > 23) { $h = 23; }
						if ($m < 0) { $m = 0; } elseif ($m > 59) { $m = 59; }
						if ($s < 0) { $s = 0; } elseif ($s > 59) { $s = 59; }
						$value = date('H:i:s', mktime($h, $m, $s));
						return true;
					}
				}
				break;
			case 'year':
				$regexp = "/^{$rexp_4}$/";
				if (preg_match($regexp, $value, $r)) {
					if (isset($r[1])) {
						$y = intval($r[1]);
						$m = intval($r[2]);
						$s = intval($r[3]);
						if ($y == 0) {
							$date = '0000';
						} elseif ($y < 100) {
							$y2k = (intval(date('Y')) - 2000) + 25;
							if ($y <= $y2k) { $value = intval('20' . $y); }
							else { $value = intval('19' . $y); }
						}
						return true;
					}
				}
				break;
			default:
				$regexp = "/^{$rexp_date}[\ ]{$rexp_time}$/";
				$date = '';
				$time = '';
				if (preg_match($regexp, $value, $r)) {
					if (isset($r[3])) {
						$y = intval($r[1]);
						$m = intval($r[2]);
						$d = intval($r[3]);
						if ($y == 0 && $m == 0 && $d == 0) {
							$date = '0000-00-00';
						} else {
							if ($y < 100) {
								$y2k = (intval(date('Y')) - 2000) + 25;
								if ($y <= $y2k) { $y = intval('20' . $y); }
								else { $y = intval('19' . $y); }
							}
							$date = date('Y-m-d', mktime(0, 0, 0, $m, $d, $y));
						}
					}
					if (isset($r[6])) {
						$h = intval($r[4]);
						$m = intval($r[5]);
						$s = intval($r[6]);
						if ($h == 0 && $m == 0 && $s == 0) {
							$time = '00:00:00';
						} else {
							if ($h < 0) { $h = 0; } elseif ($h > 23) { $h = 23; }
							if ($m < 0) { $m = 0; } elseif ($m > 59) { $m = 59; }
							if ($s < 0) { $s = 0; } elseif ($s > 59) { $s = 59; }
							$time = date('H:i:s', mktime($h, $m, $s));
						}
					}
				}
				$value = trim($date . ' ' . $time);
				return ($date != '' && $time != '');
				break;
		}
		return false;
	}

	# ###########################################################################

	/**
	* Numeric validator
	*
	* @param integer or float $value (pass through)
	* @param bool $is_float Validate as float
	* @param bool $unsigned Validate as unsigned
	* @param bool $float_error The value includes a float error
	* @return bool
	*/
	static function numeric_validator(&$value = 0, $is_float = false, $unsigned = false, $float_error = false)
	{
		$rexp_int = "[0-9]+";
		$rexp_dec = "([0-9]*[\.]{$rexp_int})|({$rexp_int}[\.][0-9]*)";
		$rexp_pn = "[\+\-]?";
		$rexp_float = "(({$rexp_int}|{$rexp_dec}))";
		$rexp_float_e = "(({$rexp_int}|{$rexp_dec})([eE][\+\-])?{$rexp_int})";
		if (!$is_float) {
			if (!is_int($value)) {
				$value = intval($value);
				return false;
			}
			$regexp = "{$rexp_int}";
			if ($unsigned) { $regexp = $rexp_pn . $regexp; }
		} else {
			if (!is_numeric($value)) {
				$value = floatval($value);
				return false;
			}
			if ($float_error) { $regexp = $rexp_float_e; }
			else { $regexp = $rexp_float; }
			if ($unsigned) { $regexp = $rexp_pn . $regexp; }
		}
		$regexp = "/^{$regexp}$/";
		return (preg_match($regexp, trim(strval($value))) !== false);
	}

	# ###########################################################################

	/**
	* Phone number validator
	*
	* @param string $value (pass through) The phone string value
	* @return bool
	*/
	static function phone_validator(&$value = '')
	{
		if (!is_string($value)) { $value = strval($value); }
		$value = trim($value);
		// Validates country regions with plus sign and round and square brackets: (+40) [+040] +40
		// Validate numbers with groups of minimum 2 digits delimited by spaces,points,dashes or commas
		$regexp = "/^([\(\[]?[\+]?[0-9]+[\)\]]?([\-\.\,\ ]+)?)?(([0-9]{2,})([\-\.\,\ ]+)?){1,}$/";
		return (preg_match($regexp, $value) !== false);
	}


	# ###########################################################################

	/**
	* Multiple validaotor
	*
	* @param mixed $value (pass through)
	* @param string $type
	* @return bool
	*/
	static function multi_validator(&$value = null, $type = '')
	{
		$type = trim(strtolower($type));
		switch ($type) {
			case 'double':
			case 'float':
			case 'int':
			case 'integer':
			case 'number':
			case 'numeric':
			case 'real':
				$tp1 = '';
				$tp2 = '';
				$args = func_num_args();
				if ($args > 2) { $tp1 = trim(strtolower(strval(func_get_arg(2)))); }
				if ($args > 3) { $tp2 = trim(strtolower(strval(func_get_arg(3)))); }
				$unsigned = ($tp1 == 'unsig' || $tp2 == 'unsig' || $tp1 == 'unsigned' || $tp2 == 'unsigned');
				$float_error = ($tp1 == 'err' || $tp2 == 'err' || $tp1 == 'error' || $tp2 == 'error');
				$is_float = ($type != 'int' && $type != 'integer');
				return numeric_validator($value, $is_float, $unsigned, $float_error);
				break;
			case 'char':
			case 'string':
			case 'text':
			case 'varchar':
				$max_len = 0;
				$args = func_num_args();
				if ($args > 2) { $max_len = intval(func_get_arg(2)); }
				return string_validator($value, $max_len, ($type == 'char'));
				break;
			case 'email':
			case 'eml':
				return email_validator($value);
				break;
			case 'url':
			case 'web':
				return url_validator($value);
				break;
			case 'family_name':
			case 'fname':
			case 'given_name':
			case 'lname':
			case 'middle_name':
			case 'mname':
			case 'name':
			case 'person':
				if (!is_string($value)) { return false; }
				$args = func_num_args();
				$arg = '';
				if ($args > 2) { $arg = trim(strtolower(strval(func_get_arg(2)))); }
				$value = trim($value);
				if (strlen($value) > 1) {
					if ($arg == 'up' || $arg = 'uc' || $arg == 'upper' ||
						$arg == 'upcase' || $arg = 'uppercase') {
						$value = strtoupper($value);
					} else {
						if (strstr($value, '-')) { $value = str_replace('-', '.', $value); }
						$value = ucwords(strtolower($value));
						if (strstr($value, '.')) { $value = str_replace('.', '-', $value); }
					}
					return true;
				}
				return false;
				break;
			case 'cell':
			case 'cellphone':
			case 'fax':
			case 'mobile':
			case 'phone':
			case 'telephone':
				if (!is_string($value)) { return false; }
				return phone_validator($value);
				break;
		}
	}

	# ###########################################################################

}
